package Tar

import (
	"archive/tar"
	"bytes"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/IRW"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

// appendTarFile
// 说明：将一个文件添加到tar文件里
// 注意：追加的文件，路径格式是类似"download/book/xhytd/151951/58753927.txt"，没有"./"，也不是windows的"\"
func appendTarFile(tarFilePath, addFilePath, fileName string) error {
	param := appendParam{}
	param.Type = TypeFile
	param.Param = addFilePath

	return appendContent2Tar(tarFilePath, param, fileName)
}

// appendTarBuffFile
// 说明：将一个文件添加到tar文件里
// 注意：追加的文件，路径格式是类似"download/book/xhytd/151951/58753927.txt"，没有"./"，也不是windows的"\"
func appendTarBuffFile(tarFilePath string, content []byte, fileName string) error {
	param := appendParam{}
	param.Type = TypeBuff
	param.Param = content

	return appendContent2Tar(tarFilePath, param, fileName)
}

func appendContent2Tar(tarFilePath string, param appendParam, fileName string) error {
	// 检测：是否存在tar文件
	headers, err := readTarHeader(tarFilePath)
	if err != nil {
		return err
	}
	_, ok := headers[fileName]
	if ok {
		return fmt.Errorf("该文件在tar中已经存在: ")
	}

	// 示例：创建tar文件并添加文件
	tarFile, err := os.OpenFile(tarFilePath, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return fmt.Errorf("创建tar文件失败: %v\n", err)
	}
	defer tarFile.Close()

	// 检查并处理结尾块
	if stat, _ := tarFile.Stat(); stat.Size() >= 1024 {
		buffer := make([]byte, 1024)
		if _, err := tarFile.ReadAt(buffer, stat.Size()-1024); err == nil {
			allZero := true
			for _, b := range buffer {
				if b != 0 {
					allZero = false
					break
				}
			}
			if allZero {
				// 移除结尾块并重新定位
				tarFile.Truncate(stat.Size() - 1024)
				tarFile.Seek(0, io.SeekEnd)
			}
		}
	}

	// 创建tar.Writer，从当前位置写入
	tarWriter := tar.NewWriter(tarFile)
	defer tarWriter.Close()

	// 添加本地文件到tar中的指定路径
	err = addContentToTar(tarWriter, param, fileName)
	if err != nil {
		return fmt.Errorf("添加文件到tar失败: %v\n", err)
	}

	return nil
}

func addContentToTar(tw *tar.Writer, param appendParam, tarPath string) error {
	// 标准化tar路径，清理多余部分并确保使用斜杠
	tarPath = path.Clean(tarPath)
	if path.IsAbs(tarPath) {
		tarPath = tarPath[1:] // 转换为相对路径
	}
	tarPath = strings.ReplaceAll(tarPath, "\\", "/") // 确保Windows路径转换

	if strings.HasPrefix(tarPath, "./") {
		return fmt.Errorf("输入的tar文件中路径名格式不符合tar的规范，正确的参考范例：download/text/book.txt")
	}

	// 分解路径为目录和文件名
	dirParts := strings.Split(tarPath, "/")
	if len(dirParts) < 1 {
		return fmt.Errorf("无效的tar路径: %s", tarPath)
	}

	// 提取父目录部分
	parentDirs := dirParts[:len(dirParts)-1]

	// 构建并添加所有父目录
	currentDir := ""
	for _, dir := range parentDirs {
		currentDir = path.Join(currentDir, dir)
		dirHeader := &tar.Header{
			Name:     currentDir + "/", // 目录以斜杠结尾
			Typeflag: tar.TypeDir,
			Mode:     0755,
			ModTime:  time.Now(),
		}
		if err := tw.WriteHeader(dirHeader); err != nil {
			return fmt.Errorf("写入目录头失败 %s: %v", currentDir, err)
		}
	}

	if param.Type == TypeFile {
		// 打开本地文件
		file, err := os.Open(param.Param.(string))
		if err != nil {
			return fmt.Errorf("无法打开本地文件 %s: %v", param.Param.(string), err)
		}
		defer file.Close()

		// 获取文件信息
		fileInfo, err := file.Stat()
		if err != nil {
			return fmt.Errorf("无法获取文件信息 %s: %v", param.Param.(string), err)
		}

		// 创建文件头
		fileHeader, err := tar.FileInfoHeader(fileInfo, "")
		if err != nil {
			return fmt.Errorf("创建文件头失败: %v", err)
		}
		fileHeader.Name = tarPath // 设置tar中的完整路径

		// 写入文件头
		if err := tw.WriteHeader(fileHeader); err != nil {
			return fmt.Errorf("写入文件头失败 %s: %v", tarPath, err)
		}

		// 写入文件内容
		if _, err := io.Copy(tw, file); err != nil {
			return fmt.Errorf("写入文件内容失败 %s: %v", tarPath, err)
		}
	}
	if param.Type == TypeBuff {
		// 创建自定义的FileInfo
		fileInfo := customFileInfo{
			name:    tarPath,
			size:    int64(len(param.Param.([]byte))),
			mode:    0755,
			modTime: time.Now(),
			isDir:   false,
		}

		// 创建tar文件头
		header, err := tar.FileInfoHeader(fileInfo, "")
		if err != nil {
			return err
		}

		// 可选：设置其他头字段
		header.Uid = 1000
		header.Gid = 1000
		header.Uname = "user"
		header.Gname = "group"

		// 创建文件头
		fileHeader, err := tar.FileInfoHeader(fileInfo, "")
		if err != nil {
			return fmt.Errorf("创建文件头失败: %v", err)
		}
		fileHeader.Name = tarPath // 设置tar中的完整路径

		// 写入文件头
		if err := tw.WriteHeader(fileHeader); err != nil {
			return fmt.Errorf("写入文件头失败 %s: %v", tarPath, err)
		}

		// 写入文件内容
		reader := IRW.NewByteReader(param.Param.([]byte))
		if _, err := io.Copy(tw, reader); err != nil {
			return fmt.Errorf("写入文件内容失败 %s: %v", tarPath, err)
		}
	}

	return nil
}

func addContentToTar1(tarFilePath string, content []byte, fileName string) {
	// 创建一个缓冲区来存储tar文件内容
	var buf bytes.Buffer

	// 创建一个新的tar写入器
	tw := tar.NewWriter(&buf)

	// 创建自定义的FileInfo
	fileInfo := customFileInfo{
		name:    fileName,
		size:    int64(len(content)),
		mode:    0755,
		modTime: time.Now(),
		isDir:   false,
	}

	// 创建tar文件头
	header, err := tar.FileInfoHeader(fileInfo, "")
	if err != nil {
		fmt.Println("创建tar头失败:", err)
		return
	}

	// 可选：设置其他头字段
	header.Uid = 1000
	header.Gid = 1000
	header.Uname = "user"
	header.Gname = "group"

	// 写入文件头
	if err := tw.WriteHeader(header); err != nil {
		fmt.Println("写入tar头失败:", err)
		return
	}

	// 写入文件内容
	if _, err := tw.Write(content); err != nil {
		fmt.Println("写入内容失败:", err)
		return
	}

	// 关闭tar写入器
	if err := tw.Close(); err != nil {
		fmt.Println("关闭tar写入器失败:", err)
		return
	}

	// 将tar内容写入文件
	if err := os.WriteFile(tarFilePath, buf.Bytes(), 0644); err != nil {
		fmt.Println("写入文件失败:", err)
		return
	}
}

func splitFileName(fileName string) (string, string) {
	// 标准化tar路径，清理多余部分并确保使用斜杠
	fileName = path.Clean(fileName)
	if path.IsAbs(fileName) {
		fileName = fileName[1:] // 转换为相对路径
	}
	fileName = strings.ReplaceAll(fileName, "\\", "/") // 确保Windows路径转换
	if strings.HasPrefix(fileName, "./") {
		fileName = fileName[2:len(fileName)]
	}

	// 分解路径为目录和文件名
	dirParts := strings.Split(fileName, "/")
	if len(dirParts) < 1 {
		return "", ""
	}

	file := dirParts[len(dirParts)-1]
	dir := fileName[:len(fileName)-len(file)]

	return dir, file
}

func addDirToTar(tw *tar.Writer, headers map[string]*tar.Header, tarPath string) error {
	// 标准化tar路径，清理多余部分并确保使用斜杠
	tarPath = path.Clean(tarPath)
	if path.IsAbs(tarPath) {
		tarPath = tarPath[1:] // 转换为相对路径
	}
	tarPath = strings.ReplaceAll(tarPath, "\\", "/") // 确保Windows路径转换

	// 分解路径为目录和文件名
	dirParts := strings.Split(tarPath, "/")
	if len(dirParts) < 1 {
		return fmt.Errorf("无效的tar路径: %s", tarPath)
	}

	// 构建并添加所有父目录
	currentDir := ""
	for _, dir := range dirParts {
		currentDir = path.Join(currentDir, dir)

		// 检测：原来的tar是否存在该目录
		if currentDir == "." {
			_, ok := headers[currentDir]
			if ok {
				continue
			}
		} else {
			_, ok := headers[currentDir+"/"]
			if ok {
				continue
			}
		}

		dirHeader := &tar.Header{
			Name:     currentDir + "/", // 目录以斜杠结尾
			Typeflag: tar.TypeDir,
			Mode:     0755,
			ModTime:  time.Now(),
		}
		if err := tw.WriteHeader(dirHeader); err != nil {
			return fmt.Errorf("写入目录头失败 %s: %v", currentDir, err)
		}
	}

	return nil
}

// createTarFile
// 说明：创建一个tar文件，包含指定目录下的所有文件和子目录
func createTarFile(dirPath, tarFilePath string) error {
	// 创建tar文件
	tarFile, err := os.Create(tarFilePath)
	if err != nil {
		return err
	}
	defer tarFile.Close()

	// 创建tar writer
	tw := tar.NewWriter(tarFile)
	defer tw.Close()

	// 遍历目录并添加文件到tar文件中
	return filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// 创建tar header
		header, err := tar.FileInfoHeader(info, info.Name())
		if err != nil {
			return err
		}

		// 调整header的Name字段，使其相对于给定目录的路径（例如：如果dirPath是"/a"，而当前文件是"/a/b/c"，则header.Name应为"b/c"）
		header.Name, err = filepath.Rel(dirPath, path)
		if err != nil {
			return err
		}

		// 写入header
		if err := tw.WriteHeader(header); err != nil {
			return err
		}

		// 如果不是目录，则写入文件内容
		if !info.Mode().IsDir() {
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()
			_, err = io.Copy(tw, file) // 将文件内容复制到tar writer中
			return err
		}
		return nil
	})
}

// extractTarFile
// 说明：将一个tar文件解压到一个特定的目录
func extractTarFile(tarPath, destPath string) error {
	// 打开tar文件
	tarFile, err := os.Open(tarPath)
	if err != nil {
		return err
	}
	defer tarFile.Close()

	// 创建一个tar.Reader来读取tar文件
	tarReader := tar.NewReader(tarFile)

	// 遍历tar文件中的所有文件和目录
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break // 没有更多文件了
		}
		if err != nil {
			return err
		}

		// 构建目标文件的完整路径
		targetPath := filepath.Join(destPath, header.Name)

		switch header.Typeflag {
		case tar.TypeDir:
			// 如果是目录，则创建目录
			if err := os.MkdirAll(targetPath, 0755); err != nil {
				return err
			}
		case tar.TypeReg:
			// 如果是文件，则创建文件并写入内容
			file, err := os.Create(targetPath)
			if err != nil {
				return err
			}
			if _, err := io.Copy(file, tarReader); err != nil {
				file.Close()
				return err
			}
			file.Close()
		}
	}

	return nil
}

// readTarHeader
// 说明：读取tar中的文件列表名称
// 注意：文件列表包含表示当前目录的.
func readTarHeader(tarFilePath string) (map[string]*tar.Header, error) {
	heads := make(map[string]*tar.Header)

	// 打开tar文件
	file, err := os.Open(tarFilePath)
	if err != nil {
		return nil, err
	}
	defer file.Close() // 确保在函数结束时关闭文件

	// 创建tar读取器
	tr := tar.NewReader(file)

	// 遍历tar文件中的所有条目
	for {
		header, err := tr.Next()
		if err != nil {
			if err == io.EOF {
				// io.EOF表示已到达文件末尾，没有更多的条目了
				break
			}
			return nil, err
		}

		// header.Name包含了文件的路径和名称
		heads[header.Name] = header
	}

	return heads, nil
}

func readTarPathHeader(tarFilePath, dirPath string) (map[string]*tar.Header, error) {
	heads := make(map[string]*tar.Header)

	// 打开tar文件
	file, err := os.Open(tarFilePath)
	if err != nil {
		return nil, err
	}
	defer file.Close() // 确保在函数结束时关闭文件

	// 创建tar读取器
	tr := tar.NewReader(file)

	// 遍历tar文件中的所有条目
	for {
		header, err := tr.Next()
		if err != nil {
			if err == io.EOF {
				// io.EOF表示已到达文件末尾，没有更多的条目了
				break
			}
			return nil, err
		}

		if !strings.HasPrefix(header.Name, dirPath) || header.Name == dirPath {
			continue
		}

		// header.Name包含了文件的路径和名称
		heads[header.Name] = header
	}

	return heads, nil
}

func readTarContent(tarFilePath string, fileName string) ([]byte, error) {
	fileNames := make(map[string][]byte)
	fileNames[fileName] = nil

	err := readTarContents(tarFilePath, fileNames, false)
	if err != nil {
		return nil, err
	}

	if fileNames[fileName] == nil {
		return nil, err
	}

	return fileNames[fileName], nil
}

func existTarContent(tarFilePath string, fileName string) (bool, error) {
	fileNames := make(map[string][]byte)
	fileNames[fileName] = nil

	err := readTarContents(tarFilePath, fileNames, true)
	if err != nil {
		return false, err
	}

	data := fileNames[fileName]
	return data != nil, err
}

func readTarContents(tarFilePath string, fileNames map[string][]byte, onlyHas bool) error {
	// 打开tar文件
	tarFile, err := os.Open(tarFilePath)
	if err != nil {
		return err
	}
	defer tarFile.Close()

	// 创建一个tar读取器
	tarReader := tar.NewReader(tarFile)

	// 循环读取tar文件中的每个条目
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			// 如果读取到EOF，则结束循环
			break
		}
		if err != nil {
			return err
		}

		// 检查文件名是否是你想要的文件名
		_, ok := fileNames[header.Name]
		if ok {
			if onlyHas {
				fileNames[header.Name] = make([]byte, 0)
			} else {
				// 读取文件内容
				content, err := io.ReadAll(tarReader)

				if err != nil {
					return err
				} else {
					fileNames[header.Name] = content
				}
			}

		}
	}

	return nil
}
